﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Windows.Forms;

namespace Cryptography_0912089_0912100
{
    public class RSACryptopher : AsymmetricCrytopher
    {
        private RSACryptoServiceProvider _rsa;
        public static String PREFIX = ".rsa";

        public RSACryptopher(string keyContainerName)
        {
            CspParameters cspParams;
            cspParams = new CspParameters();
            cspParams.KeyContainerName = keyContainerName;
            cspParams.Flags = CspProviderFlags.UseMachineKeyStore;

            _rsa = new RSACryptoServiceProvider(cspParams);
        }

        public RSACryptopher()
        {
            _rsa = new RSACryptoServiceProvider();
        }

        public override string GeneratePrivateKey(string keyContainerName)
        {
            // Create the CspParameters object and set the key container 
            // name used to store the RSA key pair.
            CspParameters cp = new CspParameters();
            cp.KeyContainerName = keyContainerName;

            // Create a new instance of RSACryptoServiceProvider that accesses
            // the key container MyKeyContainerName.
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);

            string stringXmlResult = rsa.ToXmlString(true);

            return stringXmlResult;
        }

        public override string GeneratePublicKey(string keyContainerName)
        {
            // Create the CspParameters object and set the key container 
            // name used to store the RSA key pair.
            CspParameters cp = new CspParameters();
            cp.KeyContainerName = keyContainerName;

            // Create a new instance of RSACryptoServiceProvider that accesses
            // the key container MyKeyContainerName.
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);

            string stringXmlResult = rsa.ToXmlString(false);

            return stringXmlResult;
        }

        public override void GeneratePrivateKey(string keyContainerName, string keyFileOutput)
        {
            StreamWriter streamWriter = new StreamWriter(keyFileOutput);
            streamWriter.Write(GeneratePrivateKey(keyContainerName));
            streamWriter.Flush();
            streamWriter.Close();
        }

        public override void GeneratePublicKey(string keyContainerName, string keyFileOutput)
        {
            StreamWriter streamWriter = new StreamWriter(keyFileOutput);
            streamWriter.Write(GeneratePublicKey(keyContainerName));
            streamWriter.Flush();
            streamWriter.Close();
        }

        public override string GenerateDigitalSignature(string fileData, string fileKeyPair)
        {
            StreamReader reader = new StreamReader(fileKeyPair);
            string privateKeyXML = reader.ReadToEnd();
            _rsa.FromXmlString(privateKeyXML);
            reader.Close();

            RSAPKCS1SignatureFormatter RSAform = new RSAPKCS1SignatureFormatter(_rsa);
            RSAform.SetHashAlgorithm("MD5");

            byte[] hashData = _md5.ComputeHash(new FileStream(fileData, FileMode.Open, FileAccess.Read, FileShare.Read));

            byte[] sign = RSAform.CreateSignature(hashData);

            StringBuilder sb = new StringBuilder();
            foreach (byte b in sign)
                sb.Append(b.ToString("x2"));

            return sb.ToString();
        }

        public bool VerifySignature(string fileOriginalData, byte[] data, string pathToKey)
        {
            StreamReader reader = new StreamReader(pathToKey);
            string publicKeyXML = reader.ReadToEnd();
            _rsa.FromXmlString(publicKeyXML);
            reader.Close();

            RSAPKCS1SignatureDeformatter RSAdeform = new RSAPKCS1SignatureDeformatter(_rsa);
            RSAdeform.SetHashAlgorithm("MD5");

            byte[] hashOriginalData = _md5.ComputeHash(new FileStream(fileOriginalData, FileMode.Open, FileAccess.Read, FileShare.Read));



            return RSAdeform.VerifySignature(hashOriginalData, data);
        }

        public override bool VerifyDigitalSignature(string fileOriginalData, string fileSign, string fileKeyPair)
        {
            StreamReader reader = new StreamReader(fileKeyPair);
            string publicKeyXML = reader.ReadToEnd();
            _rsa.FromXmlString(publicKeyXML);
            reader.Close();

            RSAPKCS1SignatureDeformatter RSAdeform = new RSAPKCS1SignatureDeformatter(_rsa);
            RSAdeform.SetHashAlgorithm("MD5");

            byte[] hashOriginalData = _md5.ComputeHash(new FileStream(fileOriginalData, FileMode.Open, FileAccess.Read, FileShare.Read));

            reader = new StreamReader(fileSign);
            string sign = reader.ReadToEnd();

            byte[] dataSign = ConvertHexStringToByte(sign);

            //byte[] dataSign = new byte[sign.Length * sizeof(char)];
            //System.Buffer.BlockCopy(sign.ToCharArray(), 0, dataSign, 0, dataSign.Length);

            return RSAdeform.VerifySignature(hashOriginalData, dataSign);
        }

        private byte[] ConvertHexStringToByte(string hexString)
        {
            byte[] result = new byte[hexString.Length / 2];

            for (int i = 0; i < result.Length; i++)
            {
                string strhex = hexString[2 * i].ToString() + hexString[2 * i + 1].ToString();
                result[i] = Convert.ToByte(strhex, 16);
            }

            return result;
        }

        public void LoadFromXML(String fileKeyPair)
        {
            StreamReader reader = new StreamReader(fileKeyPair);
            string KeyXML = reader.ReadToEnd();
            _rsa.FromXmlString(KeyXML);
        }

        public override void Encrypt(object input)
        {
            try
            {
                String[] info = (String[])input;

                String outpath = info[1];
                outpath += "\\";
                outpath += Path.GetFileName(info[0]) + PREFIX;

                byte[] byteIn = File.ReadAllBytes(info[0]);

                //byte[] encryptedData = this._rsa.Encrypt(byteIn, true);

                // by default this will create a 128 bits AES (Rijndael) object
                SymmetricAlgorithm sa = SymmetricAlgorithm.Create();
                ICryptoTransform ct = sa.CreateEncryptor();
                byte[] encrypt = ct.TransformFinalBlock(byteIn, 0, byteIn.Length);

                RSAPKCS1KeyExchangeFormatter fmt = new RSAPKCS1KeyExchangeFormatter(_rsa);
                byte[] keyex = fmt.CreateKeyExchange(sa.Key);

                // return the key exchange, the IV (public) and encrypted data
                byte[] result = new byte[keyex.Length + sa.IV.Length + encrypt.Length];
                Buffer.BlockCopy(keyex, 0, result, 0, keyex.Length);
                Buffer.BlockCopy(sa.IV, 0, result, keyex.Length, sa.IV.Length);
                Buffer.BlockCopy(encrypt, 0, result, keyex.Length + sa.IV.Length, encrypt.Length);

                File.WriteAllBytes(outpath, result);

                MessageBox.Show("Success!!!",
                                "Infomation",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Information);
            }
            catch (CryptographicException ex)
            {
                MessageBox.Show(ex.Message,
                                "Error",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
            }
        }

        public override void Decrypt(object input)
        {
            try
            {
                String[] info = (String[])input;

                String outpath = info[1];
                outpath += "\\";
                outpath += Path.GetFileName(info[0]);
                outpath = outpath.Replace(PREFIX, "");

                byte[] byteIn = File.ReadAllBytes(info[0]);

                //byte[] encryptedData = this._rsa.Encrypt(byteIn, true);

                // by default this will create a 128 bits AES (Rijndael) object
                SymmetricAlgorithm sa = SymmetricAlgorithm.Create();

                byte[] keyex = new byte[_rsa.KeySize >> 3];
                Buffer.BlockCopy(byteIn, 0, keyex, 0, keyex.Length);

                RSAPKCS1KeyExchangeDeformatter def = new RSAPKCS1KeyExchangeDeformatter(_rsa);
                byte[] key = def.DecryptKeyExchange(keyex);

                byte[] iv = new byte[sa.IV.Length];
                Buffer.BlockCopy(byteIn, keyex.Length, iv, 0, iv.Length);

                ICryptoTransform ct = sa.CreateDecryptor(key, iv);
                byte[] decrypt = ct.TransformFinalBlock(byteIn, keyex.Length + iv.Length, byteIn.Length - (keyex.Length + iv.Length));

                File.WriteAllBytes(outpath, decrypt);

                MessageBox.Show("Success!!!",
                                "Infomation",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Information);
            }
            catch (CryptographicException ex)
            {
                MessageBox.Show(ex.Message,
                                "Error",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
            }
        }
        
    }
}
